home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / which213.zip / src-org / which.c < prev    next >
C/C++ Source or Header  |  1993-07-28  |  21KB  |  574 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.     which.c
  4.  
  5.     This program by default finds the path of an executable command under
  6.     either OS/2 or MS-DOS.  It can also be used to find data files in the
  7.     DPATH or DLLs in the LIBPATH (at least under OS/2).  It does not have
  8.     a whole lot of error-checking, but the array sizes are reasonably gen-
  9.     erous.
  10.  
  11.     To do:  add support for ksh, bash, etc.; allow wildcard commands?
  12.  
  13.     Copyright (c) 1993 by Greg Roelofs.  4OS2/4DOS spawnl() code by Michael
  14.     D. Lawler.  DosQuerySysInfo() code by Kenneth Porter.  You may use this
  15.     code for any purpose whatsoever, as long as you don't sell it and don't
  16.     claim to have written it.  Not that you'd want to.
  17.  
  18.   ---------------------------------------------------------------------------*/
  19.  
  20. #define VERSION   "v2.1 of 28 July 1993"
  21.  
  22. /* #define DEBUG */
  23.  
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <ctype.h>
  27. #include <process.h>
  28. #include <string.h>
  29. #include <sys/types.h>
  30. #include <sys/stat.h>
  31.  
  32. #ifndef TRUE
  33. #  define TRUE   1
  34. #  define FALSE  0
  35. #endif
  36.  
  37. /* not used:
  38.     #if defined(__OS2__) && !defined(OS2)
  39.     #  define OS2
  40.     #endif
  41.  */
  42.  
  43. #if defined(__IBMC__)
  44. #  define S_IFMT   0xF000   /* or (S_IFREG | S_IFDIR | S_IFCHR) */
  45. #endif
  46. #ifndef S_ISDIR
  47. #  define S_ISDIR(m)   (((m) & S_IFMT) == S_IFDIR)
  48. #endif
  49.  
  50. #if defined(__IBMC__) || defined(__EMX__) || defined(__WATCOMC__)
  51. #  ifndef __32BIT__
  52. #    define __32BIT__
  53. #  endif
  54. #  define INCL_DOSMISC       /* for IBM Toolkit headers */
  55. #  define INCL_DOSERRORS     /* for IBM Toolkit headers */
  56. #  define INCL_NOPMAPI       /* for IBM Toolkit headers */
  57. #  include <os2.h>
  58. #endif
  59.  
  60. #ifdef DEBUG
  61. #  define Trace(x)   fprintf x
  62. #else
  63. #  define Trace(x)
  64. #endif
  65.  
  66.  
  67. int get_libpath(char **path);      /* our sole prototype... */
  68.  
  69.  
  70. struct stat statbuf;
  71.  
  72.  
  73. /* extensions must be in order of operating-system precedence! */
  74.  
  75. static char *ext_command[] = {     /* COMMAND extensions */
  76.     ".com",
  77.     ".exe",
  78.     ".bat"
  79. };
  80.  
  81. static char *ext_cmd[] = {         /* CMD extensions */
  82.     ".com",
  83.     ".exe",
  84.     ".cmd",
  85.     ".bat"
  86. };
  87.  
  88. static char *ext_4dos[] = {        /* 4DOS extensions */
  89.     ".com",
  90.     ".exe",
  91.     ".btm",
  92.     ".bat"
  93. };
  94.  
  95. static char *ext_4os2[] = {        /* 4OS2 extensions */
  96.     ".com",
  97.     ".exe",
  98.     ".btm",
  99.     ".cmd",
  100.     ".bat"
  101. };
  102.  
  103. static char *ext_libpath[] = {     /* LIBPATH extension(s) */
  104.     ".dll"
  105. };
  106.  
  107. static char *ext_dpath[] = {       /* DPATH extension(s) */
  108.     ".boo",
  109.     ".dat",
  110.     ".inf",
  111.     ".ini",   /* does this belong here? */
  112.     ".hlp",
  113.     ".msg",
  114.     ".ndx"
  115. };
  116.  
  117.  
  118. /* OS/2 internal commands:  some of these are rarely used outside of
  119.  * command files, but all of them can be called from the command line.
  120.  * (I don't know if some of the OS/2-only commands might be in later
  121.  * versions of MS-DOS.  One could use spawnl() and check the errors,
  122.  * but that would be slow.)
  123.  */
  124.  
  125. static char *int_command[] = {
  126.     "call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
  127.     "erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
  128.     "prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
  129.     "time", "type", "ver", "verify", "vol"
  130. };
  131.  
  132. static char *int_cmd[] = {
  133.  
  134.     /* note that extproc cannot be called from command line in 4OS2 */
  135.     "chcp", "detach", "dpath", "endlocal", "extproc", "keys", "move",
  136.     "setlocal", "start",
  137.  
  138.     /* all the rest are identical to int_command[] */
  139.     "call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
  140.     "erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
  141.     "prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
  142.     "time", "type", "ver", "verify", "vol"
  143. };
  144.  
  145.  
  146. typedef struct shellinfo {
  147.     char *name;
  148.     char **extension;
  149.     int num_ext;
  150.     char **internal;
  151.     int num_int;
  152. } SHELLINFO;
  153.  
  154. /* these guys must match the order of shells[] */
  155. #define _COMMAND   0
  156. #define _CMD       1
  157. #define _4DOS      2
  158. #define _4OS2      3
  159. #define _4OS2_16   4
  160. #define _4OS2_32   5
  161.  
  162. SHELLINFO shells[] = {
  163.     {"COMMAND.COM", ext_command,   sizeof(ext_command)/sizeof(char *),
  164.                     int_command,   sizeof(int_command)/sizeof(char *) },
  165.     {"CMD.EXE",     ext_cmd,       sizeof(ext_cmd)/sizeof(char *),
  166.                     int_cmd,       sizeof(int_cmd)/sizeof(char *) },
  167.     {"4DOS.COM",    ext_4dos,      sizeof(ext_4dos)/sizeof(char *),
  168.                     (char **)NULL, 0 },
  169.     {"4OS2.EXE",    ext_4os2,      sizeof(ext_4os2)/sizeof(char *),
  170.                     (char **)NULL, 0 },
  171.     {"4OS2-16.EXE", ext_4os2,      sizeof(ext_4os2)/sizeof(char *),
  172.                     (char **)NULL, 0 },
  173.     {"4OS2-32.EXE", ext_4os2,      sizeof(ext_4os2)/sizeof(char *),
  174.                     (char **)NULL, 0 }
  175. };
  176.  
  177. #define REGPATH    0   /* for pathtype */
  178. #define LIBPATH    1
  179. #define DPATH      2
  180.  
  181. char *prognam;
  182.  
  183.  
  184. /****************/
  185. /* Main program */
  186. /****************/
  187.  
  188. int main(int argc, char *argv[])
  189. {
  190.     static char tempname[1024];
  191.     char *p, *q, *comspec, *path, *pathvar, *dir[1000];
  192.     int i, j, k, c, error=0, startdir=0;
  193.     int sh, all=FALSE, pathtype=REGPATH, regpath, fourshell=FALSE;
  194.     int numdirs=0, numshells=sizeof(shells)/sizeof(SHELLINFO);
  195.  
  196.  
  197. /*---------------------------------------------------------------------------
  198.     Parse the command line...
  199.   ---------------------------------------------------------------------------*/
  200.  
  201.     prognam = argv[0];
  202.     p = prognam - 1;
  203.     while (*++p)
  204.         *p = tolower(*p);   /* assumes "smart" tolower() */
  205.  
  206.     if (argc <= 1)
  207.         --argc;
  208.     else
  209.         while (--argc > 0  &&  (*++argv)[0] == '-')
  210.             while ((c = *++(argv[0])) != '\0')
  211.                 switch (c) {
  212.                     case 'a':             /* list all matches */
  213.                         all = TRUE;
  214.                         break;
  215.                     case 'l':             /* search for DLLs */
  216.                         pathtype = LIBPATH;
  217.                         break;
  218.                     case 'd':             /* search for data files */
  219.                         pathtype = DPATH;
  220.                         break;
  221.                     default:
  222.                         ++error;
  223.                         break;
  224.                 } /* end switch, while, while, if */
  225.  
  226. /*---------------------------------------------------------------------------
  227.     Print usage if any errors or if no arguments.
  228.   ---------------------------------------------------------------------------*/
  229.  
  230.     if (error || (argc <= 0)) {
  231. #ifdef TRADITIONAL
  232.         fprintf(stderr, "%s: too few arguments\n", prognam);
  233. #else
  234.         fprintf(stderr, "\n\
  235. which (%s) for OS/2 %s, from Newtware\n\n\
  236. usage:  %s [ -a ] [ -l | -d ] cmd [ cmd ... ]\n\
  237. (default behavior is to find location of program executed as \"cmd\")\n\
  238.   -a  list all matches, not just first\n\
  239.   -l  search directories in LIBPATH for DLLs%s\n\
  240.   -d  search directories in DPATH for data files%s\n", VERSION,
  241. #ifdef __32BIT__
  242.           "2.x", prognam, "", "");
  243. #else
  244.           "and MS-DOS", prognam, " (OS/2)", " (OS/2)");
  245. #endif /* ?__32BIT__ */
  246. #endif /* ?TRADITIONAL */
  247.         exit(1);
  248.     }
  249.  
  250. /*---------------------------------------------------------------------------
  251.     Try to figure out what shell we're in, based on COMSPEC.
  252.   ---------------------------------------------------------------------------*/
  253.  
  254.     if ((comspec = getenv("COMSPEC")) == (char *)NULL || *comspec == '\0') {
  255.         Trace((stderr, "COMSPEC is empty...assuming COMSPEC = cmd.exe\n"));
  256.         sh = _CMD;
  257.     } else {
  258.         Trace((stderr, "COMSPEC = %s\n", comspec));
  259.         p = strrchr(comspec, '\\');
  260.         if (p == NULL)
  261.             p = comspec;
  262.         else
  263.             ++p;
  264.         for (i = 0;  i < numshells;  ++i) {
  265.             if (stricmp(p, shells[i].name) == 0) {
  266.                 sh = i;
  267.                 break;
  268.             }
  269.         }
  270.         if (i == numshells) {
  271.             sh = _CMD;
  272.             fprintf(stderr,
  273.               "%s: unknown command shell \"%s\"\n%s: assuming %s\n",
  274.               prognam, comspec, prognam, shells[sh].name);
  275.         }
  276.         if (sh >= 2 && sh <= 5)   /* 4DOS, 4OS2, 4OS2-16, 4OS2-32 */
  277.             fourshell = TRUE;
  278.     }
  279.     Trace((stderr, "shell is %s\n\n", shells[sh].name));
  280.  
  281. /*---------------------------------------------------------------------------
  282.     Get the PATH, DPATH or LIBPATH, depending on the user's wishes.
  283.   ---------------------------------------------------------------------------*/
  284.  
  285.     /* for regular path, current directory is always implied; not for others */
  286.     switch (pathtype) {
  287.         case REGPATH:
  288.             pathvar = "PATH";
  289.             regpath = TRUE;
  290.             dir[numdirs++] = ".";
  291.             path = getenv(pathvar);
  292.             break;
  293.         case DPATH:
  294.             pathvar = "DPATH";
  295.             regpath = FALSE;
  296.             shells[sh].extension = ext_dpath;
  297.             shells[sh].num_ext = sizeof(ext_dpath)/sizeof(char *);
  298.             path = getenv(pathvar);
  299.             break;
  300.         case LIBPATH:
  301.             pathvar = "LIBPATH";
  302.             regpath = FALSE;
  303.             shells[sh].extension = ext_libpath;
  304.             shells[sh].num_ext = sizeof(ext_libpath)/sizeof(char *);
  305.             get_libpath(&path);
  306.             break;
  307.     }
  308.     Trace((stderr, "COMSPEC now = %s\n", comspec));
  309.  
  310. /*---------------------------------------------------------------------------
  311.     Terminate the path elements and store pointers to each one.
  312.   ---------------------------------------------------------------------------*/
  313.  
  314.     if (path == (char *)NULL || *path == '\0') {
  315.         Trace((stderr, "\n%s is empty\n\n", pathvar));
  316.         if (!regpath) {
  317.             fprintf(stderr, "%s: %s is empty\n", prognam, pathvar);
  318.             exit(2);
  319.         }
  320.     } else {
  321.         Trace((stderr, "\n%s = %s\n\n", pathvar, path));
  322.         if (*path != ';')
  323.             dir[numdirs++] = path;
  324.         p = path - 1;
  325.         while (*++p)
  326.             if (*p == ';') {
  327.                 *p = '\0';
  328.                 if ((p[1] != '\0') && (p[1] != ';'))
  329.                     dir[numdirs++] = p + 1;
  330.             } else
  331.                 *p = tolower(*p);  /* should probably make this an option... */
  332.     }
  333.  
  334. /*---------------------------------------------------------------------------
  335.     If we're doing a normal PATH search under 4OS2 or 4DOS, check the path
  336.     for a "." entry; if find one, ignore the extra "." entry which was previ-
  337.     ously inserted at the beginning of the dir[] array.  (Entries of the form
  338.     "d:." don't count, and CMD and COMMAND always insert the "." first.)
  339.   ---------------------------------------------------------------------------*/
  340.  
  341.     if (fourshell && pathtype == REGPATH) {
  342.         for (i = 1;  i < numdirs;  ++i)
  343.             if (dir[i][0] == '.' && dir[i][1] == '\0') {    /* "." */
  344.                 startdir = 1;
  345.                 break;
  346.             }
  347.     }
  348.  
  349. /*---------------------------------------------------------------------------
  350.     For each command or file given as an argument, check all of the direc-
  351.     tories in the appropriate path.  For commands, first see if it's an in-
  352.     ternal command or (in the case of 4DOS/4OS2) an alias; if not, search
  353.     the path for it.  For each directory in the path, see if the OS will con-
  354.     sider it a command as is (it has a dot in its name), and if so whether
  355.     it exists; then try appending each extension (in order of precedence)
  356.     and again check for existence.  For data files or DLLs, just check direc-
  357.     tories in the path for the filename or the filename with an appropriate
  358.     extension (".dll", ".inf", etc.) appended.
  359.   ---------------------------------------------------------------------------*/
  360.  
  361.     for (j = 0;  j < argc;  ++j) {
  362.         int hasdot, found=0;
  363.  
  364.         /* don't bother with internal commands if argument has a dot */
  365.         hasdot = (strchr(argv[j], (int)'.') != (char *)NULL);
  366.  
  367.         if (regpath && !hasdot) {
  368.             Trace((stderr, "checking %s internals\n", shells[sh].name));
  369.  
  370.             /* 4DOS/4OS2 have tests for internals/aliases, so check smartly */
  371.             if (fourshell) {
  372.                 char *tempenv[2] = {tempname, NULL};
  373.                 int rc;
  374.  
  375. #ifdef COMMANDSEP_WORKS   /* This form always fails (get correct response only
  376.                            * if first condition is true, i.e., both alias and
  377.                            * internal command).  It appears to be another bug
  378.                            * in 4OS2 (rc always = 0).  The line is too long for
  379.                            * 4DOS in any case (257 bytes for 5-char alias name).
  380.                            */
  381.                 sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal %s "
  382.                   "then %%+ echos %s:  aliased to \"%%@alias[%s]\" %%+ exit 43 %%+"
  383.                   " elseiff isalias %s then %%+ echos %s:  aliased to \"%%@alias"
  384.                   "[%s]\" %%+ exit 42 %%+ elseiff isinternal %s then %%+ exit 41 "
  385.                   "%%+ else %%+ exit 40 %%+ endiff", argv[j], argv[j],
  386.                   argv[j], argv[j], argv[j], argv[j], argv[j], argv[j]);
  387.                 Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
  388.                   strlen(tempname), tempname));
  389.                 rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%", NULL,
  390.                   tempenv);
  391.                 Trace((stderr, "4WHICH return code = %d\n", rc));
  392. #else /* !COMMANDSEP_WORKS */
  393.                 /* quotes around alias necessary due to 4DOS/4OS2 bug */
  394.                 if (sh != _4DOS) {   /* 4OS2:  can do in one long command */
  395.                     sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal "
  396.                       "%s then & echos %s:  aliased to \"%%@alias[%s]\" & exit "
  397.                       "43 & elseiff isalias %s then & echos %s:  aliased to "
  398.                       "\"%%@alias[%s]\" & exit 42 & elseiff isinternal %s then "
  399.                       "& exit 41 & else & exit 40 & endiff", argv[j], argv[j],
  400.                       argv[j], argv[j], argv[j], argv[j], argv[j], argv[j]);
  401.                     Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
  402.                       strlen(tempname), tempname));
  403.                     /* "/c", "4which" arguments must be separate for emx+gcc */
  404.                     rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%",
  405.                       NULL, tempenv);
  406.                     Trace((stderr, "4WHICH return code = %d\n", rc));
  407.                     if (rc == 42 || rc == 43)  /* alias (and internal if 43) */
  408.                         ++found;
  409.                 } else {
  410.                     sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal "
  411.                       "%s then ^ exit 43 ^ elseiff isalias %s then ^ exit 42 ^ "
  412.                       "elseiff isinternal %s then ^ exit 41 ^ else ^ exit 40 ^ "
  413.                       "endiff", argv[j], argv[j], argv[j], argv[j]);
  414.                     Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
  415.                       strlen(tempname), tempname));
  416.                     rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%", NULL,
  417.                       tempenv);
  418.                     Trace((stderr, "4WHICH return code = %d\n", rc));
  419.                     if (rc == 42 || rc == 43) {   /* alias */
  420.                         ++found;
  421.                         sprintf(tempname, "echos %s:  aliased to \"%%@alias"
  422.                           "[%s]\"", argv[j], argv[j]);
  423.                         Trace((stderr, "2nd cmd length = %d bytes\n\n%s\n\n",
  424.                           strlen(tempname), tempname));
  425.                         spawnl(P_WAIT, comspec, comspec, "/c", tempname, NULL);
  426.                     }
  427.                 }
  428. #endif /* ?COMMANDSEP_WORKS */
  429.                 if (rc == 41 || (rc == 43 && all)) {   /* internal */
  430.                     ++found;
  431.                     sprintf(tempname, "%s internal command", shells[sh].name);
  432.                     if (rc == 41)
  433.                         printf("%s:  %s", argv[j], tempname);
  434.                     else
  435.                         printf(" (also %s", tempname);
  436.                 }
  437.             } else {
  438.                 /* quit as soon as found:  only one internal match allowed */
  439.                 for (i = 0;  (i < shells[sh].num_int) && !found;  ++i) {
  440.                     Trace((stderr, "checking %s\n", shells[sh].internal[i]));
  441.                     if (stricmp(argv[j], shells[sh].internal[i]) == 0) {
  442.                         ++found;
  443.                         printf("%s:  %s internal command", argv[j],
  444.                           shells[sh].name);
  445.                         break;
  446.                     }
  447.                 }
  448.             }
  449.         }
  450.         for (i = startdir;  (i < numdirs) && (!found || all);  ++i) {
  451.             p = tempname;
  452.             q = dir[i];
  453.             while ((*p++ = *q++) != '\0');  /* p now points to char *after* 0 */
  454.             if (p[-2] == '\\')              /* could be root dir (e.g., c:\) */
  455.                 --p;
  456.             else                            /* replace null with dir sep. */
  457.                 p[-1] = '\\';
  458.             q = argv[j];
  459.             while ((*p++ = *q++) != '\0');  /* copy program name */
  460.             --p;                            /* point at null */
  461.             if (!regpath || hasdot) {
  462.                 Trace((stderr, "checking %s\n", tempname));
  463.                 if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
  464.                     if (!found || all) {
  465.                         if (found == 1)
  466.                             printf(" (also %s", tempname);
  467.                         else
  468.                             printf("%s%s", found? ", " : "", tempname);
  469.                     }
  470.                     ++found;
  471.                     if (!all)
  472.                         break;   /* quit right now unless finding all */
  473.                 }
  474.             }
  475.             for (k = 0;  (k < shells[sh].num_ext) && (!found || all); ++k) {
  476.                 strcpy(p, shells[sh].extension[k]);
  477.                 Trace((stderr, "checking %s\n", tempname));
  478.                 if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
  479.                     if (!found || all) {
  480.                         if (found == 1)
  481.                             printf(" (also %s", tempname);
  482.                         else
  483.                             printf("%s%s", found? ", " : "", tempname);
  484.                     }
  485.                     ++found;
  486.                     if (!all)
  487.                         break;   /* quit right now unless finding all */
  488.                 }
  489.             }
  490.         } /* end i-loop */
  491.  
  492.         if (!found) {
  493.             printf("no %s in", argv[j]);
  494.             for (i = 0;  i < numdirs;  ++i)
  495.                 printf(" %s", dir[i]);
  496.         }
  497.         printf("%s\n", (found > 1)? ")" : "");
  498.     }
  499.  
  500.     return 0;
  501. }
  502.  
  503.  
  504.  
  505. /**************************/
  506. /* Function get_libpath() */
  507. /**************************/
  508.  
  509. int get_libpath(char **path)
  510. {
  511.     char *line, *p, config_sys[]="c:\\config.sys";
  512.     int foundcfg=FALSE;
  513. #ifdef __32BIT__
  514.     int drive;
  515.     APIRET rc;
  516. #endif
  517.     FILE *cfg;
  518.  
  519.  
  520.     if ((line = (char *)malloc(4096)) == NULL) {
  521.         fprintf(stderr, "%s: not enough memory\n", prognam);
  522.         exit(8);
  523.     }
  524.  
  525. #ifdef __32BIT__
  526.     rc = DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &drive, sizeof(drive));
  527.     if (rc == NO_ERROR) {
  528.         config_sys[0] = drive - 1 + 'a';
  529.         if (!stat(config_sys, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
  530.             (cfg = fopen(config_sys, "r")) != NULL)
  531.                 foundcfg = TRUE;
  532.     } else {
  533.         fprintf(stderr, "%s: internal error (%d)\n", prognam, rc);
  534. #endif
  535.         if (!stat(config_sys+2, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
  536.             (cfg = fopen(config_sys+2, "r")) != NULL)
  537.                 foundcfg = TRUE;
  538.         else if (!stat(config_sys, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
  539.             (cfg = fopen(config_sys, "r")) != NULL)
  540.                 foundcfg = TRUE;
  541. #ifdef __32BIT__
  542.     }
  543. #endif
  544.  
  545.     if (!foundcfg) {
  546.         fprintf(stderr, "%s: can't find config.sys\n", prognam);
  547.         *path = (char *)NULL;
  548.         return -1;
  549.     }
  550.  
  551.     /* assume cfg is open file pointer to config.sys */
  552.     while (fgets(line, 4096, cfg)) {
  553.         if (strnicmp(line, "LIBPATH", 7))  /* assumes no leading spaces: ? */
  554.             continue;
  555.  
  556.         /* get rid of trailing newline, if any */
  557.         if ((p=strrchr(line, '\n')) != NULL)
  558.             *p = '\0';
  559.         Trace((stderr, "found LIBPATH line:\n%s\n", line));
  560.         p = strchr(line+7, '=');
  561.         if (p == NULL) {
  562.             *path = (char *)NULL;
  563.             return -1;
  564.         }
  565.         while (*++p == ' ');
  566.         *path = p;
  567.         return 0;
  568.     }
  569.  
  570.     /* LIBPATH not found in config.sys */
  571.     *path = (char *)NULL;
  572.     return -1;
  573. }
  574.